package it.unibo.contact.EnvSystem;

import it.unibo.is.interfaces.IMessage;
import it.unibo.is.interfaces.platforms.IMessageAndContext;

import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import biz.source_code.base64Coder.Base64Coder;

public class Environment extends EnvironmentSupport 
{
	HashMap<String, String> keysForSender = new HashMap<String, String>();
	HashMap<String, Object> dataSet = new HashMap<String, Object>();
	HashMap<String, HashSet<String>> keyObservers = new HashMap<String, HashSet<String>>();
	HashSet<String> envObservers = new HashSet<String>();

	public Environment() throws Exception 
	{
		super();
	}

	@Override
	public void doJob() throws Exception 
	{	
		while(true) 
		{
			IMessage m = selectOneInput();
			if(m.msgId().equals("getData"))
			{
				showMsg("getData: [" + m.msgContent() + "] from " + m.msgEmitter());
				String value = getData(m.msgContent());
				curRequest.replyToCaller(value);
			}
			else if (m.msgId().equals("putData")) 
			{
				showMsg("putData: [" + extractKeyAndValue(m.msgContent())[0] + ":" + extractKeyAndValue(m.msgContent())[1] + 
						"] from " + m.msgEmitter());

				putData(m.msgEmitter(),extractKeyAndValue(m.msgContent())[0], extractKeyAndValue(m.msgContent())[1]);
			}
			else if(m.msgId().equals("observeData"))
			{
				registerKeyObserver(m.msgEmitter(), m.msgContent());
			}
			else if(m.msgId().equals("observeEnvironment"))
			{
				registerEnvironmentObserver(m.msgEmitter());
			}
		}
	}

	private void registerEnvironmentObserver(String msgEmitter) 
	{
		envObservers.add(msgEmitter);
	}

	private void registerKeyObserver(String msgEmitter, String key) 
	{
		if(keyObservers.containsKey(key))
		{
			keyObservers.get(key).add(msgEmitter);
		}
		else
		{
			keyObservers.put(key, new HashSet<String>());
			keyObservers.get(key).add(msgEmitter);
		}
	}

	/** Read the object from Base64 string. */
	private Object fromString( String s ) {
		Object o = null;
		try
		{
			byte [] data = Base64Coder.decode( s );
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
			o  = ois.readObject();
			ois.close();
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}

		return o;
	}

	private void putData(String mitt, String key, String value)
	{
		if(keysForSender.containsKey(key))
		{
			if(keysForSender.get(key).equals(mitt))
				dataSet.put(key, fromString(value));
		}
		else
		{
			keysForSender.put(key, mitt);
			dataSet.put(key, fromString(value));
		}

		OnChanged(key);
	}

	private void OnChanged(String key) 
	{
		NotifyKeyObservers(key);
		NotifyEnvironmentObservers(key);
	}

	private void NotifyEnvironmentObservers(String key) 
	{
		if(!(envObservers.isEmpty()))
		{
			for(String observer : envObservers)
			{
				observer = observer.replace("proxy", "");
				try 
				{
					hl_environment_forward_dataChanged_process(key, observer);
				} 
				catch (Exception e) 
				{
					e.printStackTrace();
				}
			}
		}
	}

	private void NotifyKeyObservers(String key) 
	{
		Set<String> observersToNotify = keyObservers.get(key);

		if(observersToNotify != null && !(observersToNotify.isEmpty()))
		{
			for(String observer : observersToNotify)
			{
				observer = observer.replace("proxy", "");
				try 
				{
					hl_environment_forward_dataChanged_process(key, observer);
				} 
				catch (Exception e) 
				{
					e.printStackTrace();
				}
			}
		}
	}


	/** Write the object to a Base64 string. */
	private String toString( Object o ) 
	{
		ByteArrayOutputStream baos = null;

		if(o instanceof Serializable)
		{
			try
			{
				baos = new ByteArrayOutputStream();
				ObjectOutputStream oos = new ObjectOutputStream( baos );
				oos.writeObject( o );
				oos.close();
			}
			catch (Exception e) 
			{
				e.printStackTrace();
			}

			return new String( Base64Coder.encode( baos.toByteArray() ) );
		}
		
		return "";	//TODO Da rivedere
	}


	private String getData(String key)
	{
		if(dataSet.containsKey(key))
			return toString(dataSet.get(key));

		return "";
	}

	private String[] extractKeyAndValue(String value)
	{
		String[] res = { value.split(":")[0].replace("'", "").trim(), value.split(":")[1].replace("'", "").trim() };
		return res;
	}

}
